#ifndef MAT_23_H
#define MAT_23_H

#include "vector.hpp"

namespace fixedpoint
{
    struct mat22;

    //2x3 matrix
    //Mostly a good space-saver instead of a common 3x3 matrix
    struct mat23
    {
public:
        //Column-major ordering
        fixpoint _11, _21, _12, _22, _13, _23;

        //Constructors default to unit matrix
        mat23(): _11(1), _21(0), _12(0), _22(1), _13(0), _23(0) {}
        //All initializers are in column-major order
        mat23(fixpoint a, fixpoint b,
              fixpoint c, fixpoint d,
              fixpoint e, fixpoint f): _11(a), _21(b), _12(c),
                _22(d), _13(e), _23(f) {}

        //Array initializers
        mat23(const fixpoint a[6]): _11(a[0]), _21(a[1]), _12(a[2]),
                _22(a[3]), _13(a[4]), _23(a[5]) {}

        //Column vector initializers
        mat23(const vec2d& a, const vec2d& b, const vec2d& c = vec2d(0,0)):
                _11(a.x), _21(a.y),
                _12(b.x), _22(b.y),
                _13(c.x), _23(b.y) {}

        //Matrix copy constructor
        mat23(const mat22& a): _11(a._11), _21(a._21),
                _12(a._12), _22(a._22),
                _13(0), _23(0) {}

        //Vector column accessors
        //Watch out, someone can use this to access arbitrary memory!
        /*
        vec2d& operator [](unsigned i) const
        {
            return *(const_cast<vec2d*>((i == 1)? (&_11)) : );
        }*/

        //Individual member access
        //Rows and columns start at 1
        /*
        fixpoint& operator ()(unsigned r, unsigned c) const
        {
            return *((&_11) + ((r-1)+2*(c-1)));
        }*/

        //Assignment operators
        mat23& operator =(const mat22& a)
        {
            _11 = a._11;
            _21 = a._21;
            _12 = a._12;
            _22 = a._22;
            _13 = 0;
            _23 = 0;
            return *this;
        }
        mat23& operator =(const fixpoint a[6])
        {
            _11 = a[0];
            _21 = a[1];
            _12 = a[2];
            _22 = a[3];
            _13 = a[4];
            _23 = a[5];
            return *this;
        }

        //Modifying assignment
        mat23& operator +=(const mat23& a)
        {
            _11 += a._11;
            _21 += a._21;
            _12 += a._12;
            _22 += a._22;
            _13 += a._13;
            _23 += a._23;
            return *this;
        }
        mat23& operator -=(const mat23& a)
        {
            _11 -= a._11;
            _21 -= a._21;
            _12 -= a._12;
            _22 -= a._22;
            _13 -= a._13;
            _23 -= a._23;
            return *this;
        }
        mat23& operator *=(const fixpoint a)
        {
            _11 *= a;
            _21 *= a;
            _12 *= a;
            _22 *= a;
            _13 *= a;
            _23 *= a;
            return *this;
        }
        mat23& operator /=(const fixpoint a)
        {
            fixpoint r = fixpoint(fixpoint::RAW, UNIT) / a;
            _11 *= r;
            _21 *= r;
            _12 *= r;
            _22 *= r;
            _13 *= r;
            _23 *= r;
            return *this;
        }

        //Multiplication works as if these were a 3x3 matrix
        //but with the 3rd row vectors as (0, 0, 1)
        mat23 operator *(const mat23& a) const
        {
            return mat23(_11*a._11 + _12*a._21,
                         _21*a._11 + _22*a._21,
                         //0 for 3rd component of column
                         _11*a._12 + _12*a._22,
                         _21*a._12 + _22*a._22,
                         //0 for 3rd component of column
                         _11*a._13 + _12*a._23 + _13,
                         _21*a._13 + _22*a._23 + _23
                         //1 for 3rd component of column
                        );
        }

        mat23& operator *=(const mat23& a)
        {
            mat23 temp(operator*(a));
            return operator=(temp);
        }

        //vector multiplication works as the vector is 3d with a 3rd component
        //of 1
        vec2d operator *(const vec2d& a) const
        {
            return vec2d(_11*a.x + _12*a.y + _13,
                         _21*a.x + _22*a.y + _23);
        }

        mat23 operator *(const mat22& a) const
        {
            return mat23(_11*a._11 + _12*a._21,
                         _21*a._11 + _22*a._21,
                         //0 for 3rd component of column
                         _11*a._12 + _12*a._22,
                         _21*a._12 + _22*a._22,
                         //0 for 3rd component of column
                         _13,
                         _23
                         //1 for 3rd component of column
                        );
        }

        mat23& operator *=(const mat22& a)
        {
            mat23 temp(operator*(a));
            return operator=(temp);
        }

        //Determinant is based on a 3x3 cast
        fixpoint det() const
        {
            return (_11*_22 - _12*_21); //It is just like the 2x2 case, apparently
        }

    };

}

#endif
